/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package lintulaskenta.rekisteri;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import lintulaskenta.entity.Havainnointitapahtuma;
import lintulaskenta.entity.Lajihavainto;
import lintulaskenta.entity.Pistevuosi;
import lintulaskenta.entity.Reitti;

/**
 *
 * @author amjaalto
 * Rajapinta kannan käyttöön, tarjoaa lisäyksen ja muokkauksen (merge-operaatio),
 * sekä kannasta haun. Haku tapahtuu asettamalla hakuehtoja. Jokaiselle taululle
 * on oma metodinsa ehtojen asettamiseen. Kun ehdot on asetettu suoritetaan
 * hakumetodi. HUOM. hakuehdot säilyvät hakujen välillä jollei ehtolistaa tyhjennä
 * (tai käytä eri rekisteri-instanssia).
 */
public class HavainnointitapahtumaRekisteri extends Rekisteri {

    ArrayList<Predicate> predikaattilista;
    EntityManager entitymanager;
    CriteriaBuilder criteriabuilder;
    CriteriaQuery<Havainnointitapahtuma> criteriaquery;
    Root<Havainnointitapahtuma> tapahtuma;

    public HavainnointitapahtumaRekisteri() {
        entitymanager = getEntityManager();
        criteriabuilder = entitymanager.getCriteriaBuilder();
        criteriaquery = criteriabuilder.createQuery(Havainnointitapahtuma.class);
        tapahtuma = criteriaquery.from(Havainnointitapahtuma.class);
        criteriaquery.select(tapahtuma);
        predikaattilista = new ArrayList();
    }

    /*
     * Anna hakuehdot hash-mappina jossa pareja: avaimena attribuutin nimi ja
     * avaimeen liittyvänä arvona ehto, jonka attribuutin on täytettävä.
     * Esimerkiksi map.put(havainnoijaNumero, 515), jolloin haku palauttaisi
     * listan kaikista havainnointitapahtumista joissa havainnoijan numero on 515.
     * Ei käytössä koska tukee vain tapahtuman hakuehtoja.
     */
    @Deprecated
    private List<Havainnointitapahtuma> haeTapahtumatEhdoilla(Map<String, Object> ehdot) throws Exception {
        EntityManager em = getEntityManager();
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Havainnointitapahtuma> cq = cb.createQuery(Havainnointitapahtuma.class);

        Root<Havainnointitapahtuma> juuri = cq.from(Havainnointitapahtuma.class);

        cq.select(juuri);

        Predicate[] predikaatit = new Predicate[ehdot.size()];
        int index = 0;
        for (String attribuutinNimi : ehdot.keySet()) {

            predikaatit[index] = cb.equal(juuri.get(attribuutinNimi), ehdot.get(attribuutinNimi));
            index++;
        }
        //cq.orderBy(cb.asc(juuri.get("vuosi")));
        cq.where(predikaatit);

        return em.createQuery(cq).getResultList();
    }

    /*
     * Ehdot haettavalle havainnointitapahtumalle annetaan HashMappina jossa on avaimena attribuutti jolle halutaan asettaa ehtoja.
     * Tähän avaimeen liittyvä arvo on se ehto joka kyseiselle attribuutille asetetaan. Esimerkiksi avain: "vuosi", arvo: 2001,
     * tällä parilla haettaisiin kaikki havainnointitapahtumat jotka on tehty vuonna 2001.
     */
    public void asetaHavainnointitapahtumanHakuehdot(Map<String, Object> ehdot) {
        for (String attribuutinNimi : ehdot.keySet()) {
            predikaattilista.add(criteriabuilder.equal(tapahtuma.get(attribuutinNimi), ehdot.get(attribuutinNimi)));
        }
    }

    /*
     * Kuten havainnointitapahtumalle asetettut ehdot, mutta nämä ehdot liittyvät havainnointitapahtuman reittiin.
     * Avaimena voi siis olla "vanhaReittiNumero" ja sitä vastaavana arvona 5001, jolloin haetaan kaikki
     * havainnointitapahtumat jotka on tehty reitillä 5001.
     */
    public void asetaHavaintotapahtumienReitinHakuehdot(Map<String, Object> ehdot) {
        Join<Havainnointitapahtuma, Reitti> reitit = tapahtuma.join("reitti");
        for (String attribuutinNimi : ehdot.keySet()) {
            predikaattilista.add(criteriabuilder.equal(reitit.get(attribuutinNimi), ehdot.get(attribuutinNimi)));
        }
    }

    
    /*
     * Tällä asetetetaan havainnointitapahtuman pistevuosien hakuehdot, tarkoittaen vaikkapa hakua:
     * avain: "biotooppikoodiNyt" ja arvona: 17, jolloin haettaisiin kaikki havainnointitapahtumat
     * joissa on havaittu biotooppia 17.
     */
    public void asetaHavaintotapahtumienPistevuosienHakuehdot(Map<String, Object> ehdot) {
        Join<Havainnointitapahtuma, Pistevuosi> pistevuodet = tapahtuma.join("pistevuodet");
        for (String attribuutinNimi : ehdot.keySet()) {
            predikaattilista.add(criteriabuilder.equal(pistevuodet.get(attribuutinNimi), ehdot.get(attribuutinNimi)));
        }
    }

    /*
     *
     * Tällä asetetaan havainnointitapahtuman lajihavaintojen ehdot, eli esimerkiksi
     * havainnointitapahtuma jolla on havaittu jotakin tiettyä lintua.
     */
    public void asetaHavaintotapahtumienLajihavaintojenHakuehdot(Map<String, Object> ehdot) {
        Join<Havainnointitapahtuma, Lajihavainto> lajihavainnot = tapahtuma.join("reitti").join("havaintopisteet").join("pistevuodet").join("lajihavainnot");
        for (String attribuutinNimi : ehdot.keySet()) {
            predikaattilista.add(criteriabuilder.equal(lajihavainnot.get(attribuutinNimi), ehdot.get(attribuutinNimi)));
        }
    }

    /*
     * Lähinnä testaukseen.
     */
    public void tyhjennaHakuEhdot() {
        predikaattilista.clear();
    }
    
    /*
     * Suoritetaan haku sen jälkeen kun halutut ehdot haulle on asetettu. Palauttaa listan havainnointitapahtumista,
     * joissa haun ehdot toteutuvat. Esimerkiksi haku havaintotapahtuman ehdoilla "vuosi" 2001 ja reitin ehdoilla
     * "vanhaReittiNumero" 5001 palautettaisiin reitin 5001 havainto vuodelta 2001 eli listan pituus olisi 1.
     * Lista on järjestetty kasvavasti ensin havainnointitapahtuman reitin numeron mukaan ja seuraavaksi
     * havainnointitapahtuman vuoden mukaan.
     */
    public List<Havainnointitapahtuma> suoritaHaku() {
        Predicate[] predikaatit = new Predicate[predikaattilista.size()];
        int index = 0;
        for (Predicate predikaatti : predikaattilista) {
            predikaatit[index] = predikaatti;
            index++;
        }
        
        List<Order> jarjestys = new ArrayList();
        jarjestys.add(criteriabuilder.asc(tapahtuma.get("reitti").get("vanhaReittiNumero")));
        jarjestys.add(criteriabuilder.asc(tapahtuma.get("vuosi")));
        
        
        criteriaquery.orderBy(jarjestys);
        criteriaquery.where(predikaatit);
        criteriaquery.distinct(true);
        return entitymanager.createQuery(criteriaquery).getResultList();
    }



    /*
     * Testimain
     */
    public static void main(String[] args) throws Exception {
        HavainnointitapahtumaRekisteri htr = new HavainnointitapahtumaRekisteri();
        System.out.println("Start");


        Map<String, Object> tapahtumaehdot = new HashMap<String, Object>();
        Map<String, Object> reittiehdot = new HashMap<String, Object>();
        Map<String, Object> pistevuosiehdot = new HashMap<String, Object>();
        Map<String, Object> lajihavaintoehdot = new HashMap<String, Object>();
//        tapahtumaehdot.put("havainnoijaNumero", 5350);
//        tapahtumaehdot.put("vuosi", 1984);
//        tapahtumaehdot.put("kuukausi", 5);
//        tapahtumaehdot.put("vuosi", 1997);
//        reittiehdot.put("vanhaReittiNumero", 5108);
//        pistevuosiehdot.put("biotooppikoodiNyt", 1);
//        lajihavaintoehdot.put("parvilaji", "NUMARQ");
        //List<Havainnointitapahtuma> havainnot = htr.haeTapahtumatEhdoilla(ehdot);
        htr.asetaHavainnointitapahtumanHakuehdot(tapahtumaehdot);
        htr.asetaHavaintotapahtumienReitinHakuehdot(reittiehdot);
        htr.asetaHavaintotapahtumienPistevuosienHakuehdot(pistevuosiehdot);
//        htr.asetaHavaintotapahtumienLajihavaintojenHakuehdot(lajihavaintoehdot);

        long alku = System.currentTimeMillis();

        List<Havainnointitapahtuma> havainnot = htr.suoritaHaku();

        System.out.println("Print");
        System.out.println("Total " + havainnot.size() + " havaintotapahtumaa.");
        for (Havainnointitapahtuma h : havainnot) {
            System.out.println("*****");
            System.out.println("Vuosi: "+h.getVuosi()+" kk: "+h.getKuukausi() + " päivä: " +h.getPaiva());
            System.out.println("Alkoi: "+h.getAlkuTunnit()+":"+h.getAlkuMinuutit() + " loppui: " +h.getLoppuTunnit()+":"+h.getLoppuMinuutit());
            System.out.println("Havainnoija: "+h.getHavainnoijaNumero());
            System.out.println("Laskentakunta "+h.getLaskentakunta());
            System.out.println("Reitin reittinumero: "+h.getReitti().getVanhaReittiNumero());
            System.out.println("Reitin ID: "+h.getReitti().getReitinNumero());
            System.out.println("Reitin koordinaatit, itä: "+h.getReitti().getItakoordinaatti()+" pohjois: "+h.getReitti().getPohjoiskoordinaatti());
            System.out.println("*****");
//            for (Pistevuosi pv : h.getPistevuodet())
//                System.out.print(pv.getVuosi()+" ");
        }
        System.out.println("Finish");
        long loppu = System.currentTimeMillis();
        System.out.println("Finish. Aikaa meni: " + (loppu - alku));

    }
}
